#pragma once
#include <iterator>
#include <exception>

using namespace std;
template<class T>
struct node
{
	T value;
	node<T> *next;

	node()
	{
		next = nullptr;
	}

	node(const T &t)
	{
		value = t;
		next = nullptr;
	}
};


template<class T>
class ForwardList
{
private:
	node<T> *head;
	node<T> *tail;
public:

	ForwardList()
	{
		head = tail = nullptr;
	}

	ForwardList(const ForwardList& obj)
	{
		head = tail = nullptr;
		node<T> *c = obj.head;

		if (obj.head == nullptr)
		{
			return;
		}

		else
		{
			do
			{
				node<T> *temp = c;
				this->insert_after(c->value);
				c = c->next;
			} while (c != nullptr);
		}
	}

	bool is_empty() { return head == nullptr; }

	T& front()
	{
		if (head != nullptr)
		{
			return head->value;
		}

		else
		{
			throw exception("ForwardList::front, empty list");
		}
	}


	T& back()
	{
		if (head != nullptr)
		{
			return tail->value;
		}

		else
		{
			throw exception("ForwardList::back, empty list");
		}

	}

	void remove(const T& value)	
	{
		node<T> * c = this->head;
		node<T> * b = nullptr;


		do
		{
			node<T> * temp = c;
			if (c->value == value)
			{
				if (c == head)
				{
					this->head = this->head->next;
					delete temp;
					if (head == nullptr)
					{
						tail = nullptr;
					}
					break;
				}

				else if (c == tail)
				{
					b = this->NodeBefore(temp);
					b->next = nullptr;
					tail = b;
					delete temp;
					break;
				}

				else
				{
					b = this->NodeBefore(temp);
					b->next = c->next;
					delete temp;
					break;
				}
			}
			c = c->next;
		} while (c != nullptr);
	}

			
	
	

	void push_front(const T& value)
	{
		if (head == nullptr)
		{
			head = tail = new node<T>(value);
		}
		else
		{
			node<T> *n = new node<T>(value);
			n->next = head;
			head = n;
		}
	}

	void pop_front()
	{

		if (head == nullptr)
		{
			throw exception("forward_list::pop_front, empty list");
		}

		if (head == tail)
		{
			delete head;
			head = tail = nullptr;
		}

		else
		{
			node<T> *tmp = head ->next;
			delete head;
			head = tmp;
		}
	}

	void clear()
	{
		if (head == nullptr)
		{
			return;
		}

		node<T> *c = head;
		do
		{
			node<T> *tmp = c;
			c = c->next;
			delete tmp;

		} while (c != nullptr);

		head = tail = nullptr;

	}


	void insert_after(const T& value)
	{
		if (head == nullptr)
		{
			head = tail = new node<T>(value);
		}

		else
		{
			node<T> *n = new node<T>(value);
			tail->next = n;
			tail = n;
		}

	}

	void erase_after()				
	{

		if (head == nullptr)
		{
			throw exception("ForwardList::erase_after, empty list");
		}

		else if (head == tail)
		{
			delete head;
			head = tail = nullptr;
		}

		else
		{
			node<T> * b = nullptr;
			b = this->NodeBefore(tail);
			delete tail;
			b->next = nullptr;
			tail = b;
		}
		

	}
	
	node<T> * NodeBefore(node<T> *n)
	{
		node<T> * b = this->head;
		do
		{
			if (b->next == n)
			{
				return b;
			}
			else if (b == n)
			{
				return b;
			}
			b = b->next;
		} while (b != nullptr);

		return nullptr;
	}

	node<T> * NodeAfter(node<T> *n)
	{
		return n->next;
	}

	void swap(ForwardList<T>& fli1)						
	{
		ForwardList<int> temp(fli1);	

		fli1.clear();

		node<T> *c = this->head;
		do
		{
			node<T> *temp = c;
			fli1.insert_after(c->value);
			c = c->next;
		} while (c != nullptr);

		this->clear();
		node<T> *c2 = temp.head;
		do
		{
			node<T> *temp2 = c2;
			this->insert_after(c2->value);
			c2 = c2->next;
		} while (c2 != nullptr);
		

	}



	void sort() 
	{
		int size = 0;
		node<T> * countsize = this->head;
		while (countsize != nullptr)
		{
			size++;
			countsize = countsize->next;
		}
		int flag = 1;
		node<T> * NodeBefore = this->head;
		node<T> * currentNode = this->head->next;
		T temp;
		
		for (int i = 0; i < size; i++)
		{
			NodeBefore = this->head;
			currentNode = this->head->next;

			for (int j = 0; j < size - 1; j++)
			{
				if (NodeBefore->value > currentNode->value)
				{
					temp = NodeBefore->value;
					NodeBefore->value = currentNode->value;
					currentNode->value = temp;
					flag = 1;		
				}
				NodeBefore = currentNode;
				currentNode = currentNode->next;
			}
		}
	}

	ForwardList& operator=(const ForwardList& right_object)
	{

		node<T> * n = this->head;
		node<T> * d = nullptr;
		while (n != nullptr)
		{
			d = n;
			n = n->next;
			delete d;
		}
		this->head = this->tail = nullptr;

		if (right_object.head != nullptr)
		{
			node<T> *c = right_object.head;
			do
			{
				node<T> *temp = c;
				this->insert_after(c->value);
				c = c->next;
			} while (c != nullptr);
		}
	
		return *this;

	}


	~ForwardList()
	{ 
		this->clear();
	}


	class iterator
	{
	public:
		typedef iterator self_type;
		typedef node<T>  value_type;
		typedef node<T>& reference;
		typedef node<T>* pointer;
		typedef std::forward_iterator_tag iterator_category;
		typedef int difference_type;

		iterator(pointer ptr) : ptr_(ptr) { } //Konstruktor
		self_type operator++()
		{
			self_type i = *this;
			ptr_ = ptr_->next;
			return i;
		}
		self_type operator++(int junk) { ptr_ = ptr_->next; return *this; }
		reference operator*() { return *ptr_; }
		pointer operator->() { return ptr_; }
		bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; }
		bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; }

	private:
		pointer ptr_;
	};

	iterator begin() { return iterator(head); }
	iterator end() { return iterator(nullptr); }
};

